package lsd.nio.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

import lsd.util.SocketUtil;

public class ClientService implements Runnable {
    private ByteBuffer    readBuffer    = ByteBuffer.allocate(64);
    private ByteBuffer    writeBuffer   = ByteBuffer.allocate(64);
    private Charset       charset       = Charset.forName("GBK");
    private SocketChannel sc            = null;
    private Selector      selector      = null;
    private boolean       sendFinish    = false;                  //发送完为ture，未完为false
    private boolean       receiveFinish = false;                  //接收完为ture，未完为false
    private static int    i             = 0;
    private int           tag           = 0;
    private int           sendCount     = 0;
    private int           readCount     = 0;
    private int           number        = 100;

    public void run() {
        try {
            init();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Thread done");
    }

    public ClientService() {
        tag = i++;
    }

    public void init() throws Exception {
        InetSocketAddress isa = new InetSocketAddress("127.0.0.1", SocketUtil.port);
        sc = SocketChannel.open(isa);
        sc.configureBlocking(false);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.limit(buffer.capacity());
        selector = Selector.open();
        sc.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, buffer);

        while (selector.select() > 0) {
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iter = keys.iterator();
            SelectionKey key = null;
            while (iter.hasNext()) {
                key = iter.next();
                iter.remove();
                if (key.isWritable()) {
                    //写
                    send(key);
                }
                if (key.isReadable()) {
                    //读
                    receive(key);
                }
                if (sendFinish == true && receiveFinish == true) {
                    key.channel().close();
                    key.cancel();

                }
                //				System.out.println("readCount:"+readCount+" sendCount:"+sendCount);//测试
            }

        }

        System.out.println("SocketChannel closed");
    }

    /**
     * 不停往服务器端输出信息，输出number条
     * @param key
     * @throws IOException
     */
    public void send(SelectionKey key) throws IOException {
        //		System.out.println("send");
        if (sendFinish == false) {
            String outputString;
            if (sendCount >= number) {
                System.out.println(tag + ":输出完毕" + "count:" + sendCount);
                sendFinish = true;
                outputString = tag + ":bye" + SocketUtil.endTag;
            } else {
                outputString = tag + ":1234567890000000123456789" + SocketUtil.endTag;
            }//if end

            writeBuffer.put(encode(outputString));
            writeBuffer.flip();
            while (writeBuffer.hasRemaining()) {
                int sum = sc.write(writeBuffer);
                //				System.out.println("sum="+sum);//查看输出量
            }//while end
            writeBuffer.compact();
            sendCount++;

            //	        key.cancel();
            //	        sc.close();
            //	        System.out.println("关闭与服务器的连接");
            //	        selector.close();
            //	        System.exit(0);
        }//if end
    }

    /**
     * 接收服务器端发来的消息，打印到console
     * @param key
     * @throws IOException
     */
    public void receive(SelectionKey key) throws Exception {
        SocketChannel sc = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        //		buffer.flip();
        buffer.limit(buffer.capacity());
        //		System.out.println("begin:"+buffer.position()+":"+buffer.limit()+":"+buffer.capacity()+":"+buffer.remaining());
        readBuffer.clear();
        //		System.out.println(readBuffer.position()+":"+readBuffer.limit()+":"+readBuffer.capacity());//测试
        //		System.out.println((buffer.capacity()-buffer.position())/buffer.capacity());//测试
        //		if(buffer.remaining()<buffer.capacity()*0.5){
        //			//全部输出
        //			print(buffer,true,key);
        //		}else
        //			print(buffer,false,key);
        print(buffer, key);

        //缓存容量大于80%，暂停读取
        //if(buffer.remaining()<buffer.capacity()*0.2) return;

        sc.read(readBuffer);

        readBuffer.flip();
        //		System.out.println("end:"+buffer.position()+":"+buffer.limit()+":"+buffer.capacity()+":"+buffer.remaining());//测试
        buffer.limit(buffer.capacity());
        buffer.put(readBuffer);

    }

    /**
     * 把附件buffer缓存的数据输出到控制台
     * @param buffer 数据缓存
     */
    private void print(ByteBuffer buffer, SelectionKey key) throws IOException,
                                                           InterruptedException {
        //		System.out.println("print :"+buffer.position()+":"+buffer.limit()+":"+buffer.capacity()+":"+buffer.remaining());//测试
        //将buffer转换为字符串bufferString
        String bufferString = "";
        //显示
        do {
            buffer.flip();
            bufferString = decode(buffer);
            //			System.out.println("length="+bufferString.length());//测试
            int end = bufferString.indexOf(SocketUtil.endTag);
            //			System.out.println("end="+end);
            //			System.out.println("print0:"+buffer.position()+":"+buffer.limit()+":"+buffer.capacity()+":"+buffer.remaining());//测试
            if (end == -1)
                return;
            //		String bufferString = decode(buffer);
            String outputString = bufferString.substring(0, end + SocketUtil.endTagLength);
            System.out.println("output:" + outputString + ":length=" + outputString.length());
            //清除buffer中显示过的数据
            ByteBuffer temp = encode(outputString);
            buffer.flip();
            //			System.out.println("print:"+buffer.position()+":"+buffer.limit()+":"+buffer.capacity()+":"+buffer.remaining());//测试
            buffer.position(temp.limit());
            buffer.compact();
            readCount++;
            int locate = outputString.indexOf("bye" + SocketUtil.endTag);
            //			System.out.println("print1:"+buffer.position()+":"+buffer.limit()+":"+buffer.capacity()+":"+buffer.remaining());//测试
            //			System.out.println("locate="+locate+" index="+bufferString.indexOf(SocketUtil.endTag) );//测试
            if (locate > 0) {
                Thread.sleep(1000);
                key.cancel();
                key.channel().close();
                System.out.println(tag + "断开与服务器的链接");
                System.exit(0);
            }
        } while (true);
    }

    public static void main(String[] args) throws Exception {
        new ClientService().init();
    }

    public String decode(ByteBuffer bb) {
        CharBuffer charBuffer = charset.decode(bb);
        return charBuffer.toString();
    }

    public ByteBuffer encode(String s) {
        return charset.encode(s);
    }
}
